package socketmvc.spring.support.driver;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import socketmvc.core.codec.ProtocolCodecHandler;
import socketmvc.core.filter.FilterHandler;
import socketmvc.core.processor.annotation.SocketCommand;
import socketmvc.core.processor.bean.ProcessorMethod;
import socketmvc.server.config.BatchIpSocketMvcConfig;
import socketmvc.spring.support.SocketMvcSpringConfig;
import socketmvc.core.codec.IPacketCodec;
import socketmvc.core.filter.IFilter;
import socketmvc.core.processor.ProcessorMethodHandler;
import socketmvc.core.processor.annotation.SocketControl;
import socketmvc.core.processor.bean.Processor;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;

@Configuration
public class SocketMvcSpringSupportConfig extends BatchIpSocketMvcConfig {

    @Autowired
    private SpringSocketMvcControlHandler processorHandler;

    private Collection<SocketMvcSpringConfig> socketMvcSpringConfigs;

    private Set<String> processorScanPackage;

    private List<Class<?>> processorClasses;

    private final FilterHandler filterHandler = new FilterHandler();

    private final ProtocolCodecHandler protocolCodecHandler = new ProtocolCodecHandler();

    public void setSocketMvcSpringConfig(Collection<SocketMvcSpringConfig> socketMvcSpringConfigs) {
        this.socketMvcSpringConfigs = socketMvcSpringConfigs;
    }

    @Override
    public List<SocketMvcSpringConfig> getIpConfigs() {
        return List.copyOf(socketMvcSpringConfigs);
    }

    @Override
    public Collection<IFilter> getFilters() {
        Collection<IFilter> filters = new HashSet<>();
        for (SocketMvcSpringConfig socketMvcSpringConfig : socketMvcSpringConfigs) {
            Collection<IFilter> fls = socketMvcSpringConfig.getFilters();
            if (fls != null){
                filters.addAll(fls);
            }
        }
        return filters;
    }

    @Override
    public Collection<IPacketCodec> getPacketCodecs() {
        Collection<IPacketCodec> packetCodecs = new HashSet<>();
        for (SocketMvcSpringConfig socketMvcSpringConfig : socketMvcSpringConfigs) {
            Collection<IPacketCodec> pcs = socketMvcSpringConfig.getPacketCodecs();
            if (pcs != null){
                packetCodecs.addAll(pcs);
            }
        }
        return packetCodecs;
    }

    @Override
    public Set<String> getProcessorScanPackage() {
        return processorScanPackage;
    }

    public void setProcessorScanPackage(Set<String> processorScanPackage) {
        this.processorScanPackage = processorScanPackage;
    }

    public List<Class<?>> getProcessorClasses() {
        return processorClasses;
    }

    public void setProcessorClasses(List<Class<?>> processorClasses) {
        this.processorClasses = processorClasses;
        this.processorHandler.setProcessorClasses(processorClasses);
    }

    @Override
    public ProcessorMethodHandler getProcessorMethodHandler() {
        return processorHandler;
    }

    /**
     * 获取报文的协议编解码器
     *
     * @return ProtocolCodecHandler
     */
    @Override
    public ProtocolCodecHandler getProtocolCodecHandler() {
        return protocolCodecHandler;
    }

    /**
     * 获取mvc过滤器
     *
     * @return ProtocolCodecHandler
     */
    @Override
    public FilterHandler getFilterHandler() {
        return filterHandler;
    }

    @Component
    static class SpringSocketMvcControlHandler extends ProcessorMethodHandler{
        @Autowired
        private AutowireCapableBeanFactory beanFactory;
        @Autowired
        private Environment environment;

        private List<Class<?>> processorClasses;

        public void setProcessorClasses(List<Class<?>> processorClasses) {
            this.processorClasses = processorClasses;
        }
        @Override
        protected List<Class<?>> scanProcessorClass(Collection<String> packages) {
            return processorClasses;
        }

        @Override
        protected <T> Processor<T> createProcessor(Class<T> cls) throws Exception {
            T instance = beanFactory.getBean(cls);
            /*
             * 不自动注入：AUTOWIRE_NO
             * 使用BeanName策略注入：AUTOWIRE_BY_NAME
             * 使用类型装配策略：AUTOWIRE_BY_TYPE
             * 使用构造器装配策略：AUTOWIRE_CONSTRUCTOR
             * 自动装配策略：AUTOWIRE_AUTODETECT
             */
            //beanFactory.autowireBeanProperties(instance, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
            //beanFactory.initializeBean(instance, cls.getName() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX);
            Processor<T> processor = new Processor<>(cls.getName(),cls,instance);
            SocketControl processorAnnotation = cls.getAnnotation(SocketControl.class);
            SocketCommand commandAnnotation = cls.getAnnotation(SocketCommand.class);
            if (processorAnnotation != null) {
                String cmd = processorAnnotation.value();
                processor.setCommand(cmd);
            }else {
                processor.setCommand("/");
            }
            if (commandAnnotation != null){
                String cmd = commandAnnotation.value();
                String old = processor.getCommand();
                processor.setCommand(Path.of(old,cmd).toString());
            }
            String springResolveCmd = environment.resolvePlaceholders(processor.getCommand());
            processor.setCommand(springResolveCmd);
            return processor;
        }


        /**
         * 创建处理器函数
         * createProcessor 函数调用完成后，会依次调用该函数创建处理器内部方法
         * @param processor 处理器
         * @param method method
         * @return 处理器内部方法
         * @throws Exception ex
         */
        @Override
        protected ProcessorMethod createProcessorMethod(Processor<?> processor, Method method) throws Exception {
            ProcessorMethod processorMethod = super.createProcessorMethod(processor, method);
            if (processorMethod != null){
                String springResolveCmd = environment.resolvePlaceholders(processorMethod.getCommand());
                processorMethod.setCommand(springResolveCmd);
            }
            return processorMethod;
        }
    }

}
